# Makefile for the Simulator
# @configure_input@

ENABLE_WERROR := true
ENABLE_LINKERSCRIPT := true
ENABLE_AUTODEPEND := true
SIM_ROOT := @builddir@
include Defines.mk

# Makefile good manners
SHELL = /bin/sh

# running variables
JUNK :=
BINS :=

#----------------------------------------------------------------------------
# File lists
#----------------------------------------------------------------------------

SRCDIRS = base base/signals base/threads util mem tm directory

# basic operation
SIMSRC := \
	AccessRepeater.cpp \
	Configuration.cpp \
	Entity.cpp \
	GDB.cpp \
	Logger.cpp \
	Pool.cpp \
	Processor.cpp \
	SimSupers.cpp \
	Simulator.cpp \
	isa_ext.cpp \
	pools.cpp \
	pthread.cpp \
	sim.cpp \
	simapi.cpp \
	syscalls.cpp \
	ticket.cpp \

# base requests and responses
SIMSRC += \
	Packet.cpp \
	Request.cpp \
	MemoryRequest.cpp \
	DataRequest.cpp \
	LoadRequest.cpp \
	StoreRequest.cpp \
	RawMemoryRequest.cpp \
	Response.cpp \

# base memory system
SIMSRC += \
	Cache.cpp \
	CacheEntry.cpp \
	CacheHash.cpp \
	LineFlags.cpp \
	LineMask.cpp \
	Memory.cpp \
	MSHR.cpp \

# threading and signals
SIMSRC += \
	PthreadKeys.cpp \
	PthreadInterface.cpp \
	PthreadInterfaceLock.cpp \
	SimulatedState.cpp \
	SigContext.cpp \
	SigHandler.cpp \
	SigManager.cpp \
	ThreadManager.cpp \

# requests need for signals, syscalls, debugging, goto_real, etc.
SIMSRC += \
	CancelRequestsRequest.cpp \
	InvalidateRequest.cpp \
	QuiesceRequest.cpp \
	QuiesceResponse.cpp \
	RawFlushRequest.cpp \
	UnQuiesceRequest.cpp \
	UpdateRequest.cpp \
	VerifyRequest.cpp \

# stats
SIMSRC += \
	Bucket.cpp \
	Counter.cpp \
	Histogram.cpp \
	SimpleStatistics.cpp \
	SpeculativeCounter.cpp \
	StatisticsCollection.cpp \


# unused?
SIMSRC += \
	CompletionRequest.cpp \
	EvictRequest.cpp \

# unused (TM) ?
SIMSRC += \
	IsDirtyRequest.cpp \

EXTRA_OBJS := \
	malloc.o \

INCLUDES := -I. -I$(SRCDIR)

TEST_BASENAMES_WORKING_COMMON 	    := orig real memcpy dl print arg simapi cpp fp io barrier pthread pthreadbasic pthread_real pthread_kill sieve  sig llsc mesi

TEST_BASENAMES_WORKING_LOCK   	    := lock java buffer

TEST_BASENAMES_NOTWORKING_COMMON := __thread

TEST_PROGS_WORKING_COMMON := $(TEST_BASENAMES_WORKING_COMMON:%=test_%)
TEST_PROGS_WORKING_LOCK   := $(TEST_BASENAMES_WORKING_LOCK:%=test_%)

TEST_PROGS_WORKING        := $(TEST_PROGS_WORKING_COMMON) $(TEST_PROGS_WORKING_LOCK)
TEST_PROGS_NOTWORKING     := $(TEST_BASENAMES_NOTWORKING_COMMON:%=test_%) $(TEST_BASENAMES_NOTWORKING_LOCK:%=test_%) $(TEST_BASENAMES_NOTWORKING_TM:%=test_%)

#----------------------------------------------------------------------------
# Default target.
#----------------------------------------------------------------------------

.PHONY: default
default: $(LIBSIM_SH)
test: 

#----------------------------------------------------------------------------
# Architecture/OS specific stuff
#----------------------------------------------------------------------------

ifeq ($(ARCH),ppc)
include $(SRCDIR)/ppc/ppc.mk
else
ifeq ($(ARCH),x86)
include $(SRCDIR)/x86/x86.mk
else
	$(error unknown architecture "$(ARCH)")
endif
endif

comma := ,
modules = $(subst $(comma), ,@modules@)
include $(modules:%=$(SRCDIR)/%/module.mk)

#----------------------------------------------------------------------------
# Misc functions
#----------------------------------------------------------------------------

# put all our source directories in VPATH
VPATH := ${patsubst %,$(SRCDIR)/%,$(SRCDIRS)}
ifeq ($(ARCH),$(ARCH_PPC))
	VPATH += $(SRCDIR)/ppc
else
ifeq ($(ARCH),$(ARCH_X86))
	VPATH += $(SRCDIR)/x86
endif
endif

SIMOBJ := ${SIMSRC:.cpp=.o}

JUNK += *.o

TEST_PROG_OBJS := $(TEST_PROGS:=.o)
TEST_PROG_DEPS := $(TEST_PROGS:=.d)
BINS += $(TEST_PROGS)

#----------------------------------------------------------------------------
# include the .d for each of the sources
#----------------------------------------------------------------------------

ALLSRCS = $(SIMSRC)
DEPS = ${ALLSRCS:.cpp=.d} ${EXTRA_OBJS:.o=.d}
-include $(DEPS) $(TEST_PROG_DEPS)
JUNK += *.d

#----------------------------------------------------------------------------
# Some common rules
#----------------------------------------------------------------------------

%.o: %.cpp
	$(CPP) $(CPPFLAGS) -c -o $@ $<

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

# assembling is OS dependant, note that Linux and AIX use cc, not as
.s.o: $*.s
ifeq ($(OS),Darwin)
	$(AS) $(ASFLAGS) -o $@ $*.s
else 
ifeq ($(OS),Linux)
	$(SED) 's/#gas2 //' < $< > $*.linux.S
	$(CC) $(ASFLAGS) -o $@ $*.linux.S
else
ifeq ($(OS),AIX)
	$(SED) 's/#gas2 //' < $*.s > $*.aix.S
	$(CC) $(ASFLAGS) -o $@ $*.aix.S
else 
	$(error I do not know how to run the assembler on "$(OS)")
endif
endif
endif

#----------------------------------------------------------------------------
# Some special rules
#----------------------------------------------------------------------------

# compile these 3rd party C files with special flags
malloc.o: $(SRCDIR)/util/malloc.c
	$(CPP) $(CPPFLAGS) -DHAVE_MORECORE=0 -DMALLOC_FAILURE_ACTION='fprintf(stderr,"MALLOC_FAILURE_ACTION\n");ABORT;' -DFOOTERS=1 -c -o $@ $<
malloc6.o: $(SRCDIR)/util/malloc6.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<

# These tests are actually C++
test_cpp.o: $(SRCDIR)/test/test_cpp.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<
test_java.o: $(SRCDIR)/test/test_java.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<
test_sieve.o: $(SRCDIR)/test/test_sieve.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<
test_pthread_kill.o: $(SRCDIR)/test/test_pthread_kill.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<
test___thread.o: $(SRCDIR)/test/test___thread.c
	$(CPP) $(CPPFLAGS) -c -o $@ $<

# This is used on linux to prevent gdb from loading the system version
# of libthread_db.so.1 which is used to understand the OS pthreads
# internals. Since we have our own pthreads implementation, this
# general causes errors and premature exits from gdb. This dummy
# version has to be earlier on the LD_LIBRARY_PATH than the system one
# so that gdb will load it instead. gdb won't be able to use it since
# it doesn't contain the right symbols but it will just warn and
# disable the feature.
#
# See llcoolj.stanford.edu:/usr/src/yellowdog/SOURCES/untarred/gdb+dejagnu-20040223/gdb/thread-db.c
ifeq ($(UNAMES),$(UNAMES_LINUX))
libthread_db.so.1: libthread_db.o
	$(CPP) $(SHLIB_LDFLAGS) -o $@ $^
endif

#----------------------------------------------------------------------------
# The libraries
#----------------------------------------------------------------------------

$(LIBSIM): $(SIMOBJ) $(EXTRA_OBJS)
	$(AR) cru $@ $^
	$(RANLIB) $@

$(LIBSIM_SH): $(SIMOBJ) $(EXTRA_OBJS)
	$(CPP) $(SHLIB_LDFLAGS) -o $@ $^
BINS += $(LIBSIM) $(LIBSIM_SH)

#----------------------------------------------------------------------------
# Testing
#----------------------------------------------------------------------------

vpath test_% . $(SRCDIR)/test

TEST_PROGS := $(sort $(TEST_PROGS_WORKING))

$(TEST_PROGS): test_%: test_%.o $(LIBSIM_SH)
	$(LD) $(LDFLAGS) -o $@ $< $(LIBS)

.PHONY: working_tests tests test test-ideal
working_tests: $(TEST_PROGS_WORKING)
tests: $(TEST_PROGS)

test-ideal: working_tests $(TEST_PROGS_WORKING_COMMON:%=%.ideal.run) $(TEST_PROGS_WORKING_LOCK:%=%.ideal.run)

ifeq ($(OS),$(OS_DARWIN))
TEST_ENV:=DYLD_BIND_AT_LAUNCH=1
else 
ifeq ($(OS),$(OS_LINUX))
TEST_ENV:=USE_LOAD_BIAS=0
else 
ifeq ($(OS),$(OS_AIX))
else 
	$(error unknown OS "$(OS)")
endif
endif
endif

DEVNULL := /dev/tty
DEVNULL := /dev/null

%.ideal.run: %
	$(TEST_ENV) ./$< --- --system ideal > $(DEVNULL)


#----------------------------------------------------------------------------
# Profiling
#----------------------------------------------------------------------------
# See http://oprofile.sourceforge.net/docs/ for basic docs including cheat sheet
# Short summary for use outside of "make profile":
#     Run this before:    sudo opcontrol --no-vmlinux; sudo opcontrol --callgraph=3; sudo opcontrol --start ; sudo opcontrol --reset
#     Run your app...:    <app-name>
#     Run this after:     sudo opcontrol --stop; sudo opcontrol --dump
#     Reporting after:    sudo opreport --symbols [--callgraph] <app-name> libSim.so libc-2.5.so no-vmlinux; sudo opannotate --source --output-dir ./tmp <app-name> libSim.so
#     Cleanup:            sudo opcontrol --shutdown
.PHONY: profile
profile:
	@if [ ! `whoami` = root ] ; then \
	    echo "ERROR: you must be root to run profiling. try 'sudo $(MAKE) profile'"; \
	    exit 1; \
        fi
	@echo Profiling...
# Do not bother profiling kernel. you need vmlinux for this not vmlinuz
	opcontrol --no-vmlinux
# Start statistics collection daemon
	opcontrol --start
# Reset stats (clear old info)
	opcontrol --reset
# Run what you want to measure
	make test-ideal
# Stop statistics collection
	opcontrol --stop
# Force stats from kernel to daemon before reporting
	opcontrol --dump
# Print time spend in libSim.so
	opreport --symbols $(LIBSIM_SH)
	opannotate --source --output-dir ./opannotate/$(LIBSIM_SH)/ $(LIBSIM_SH)
# Clears stats and shutsdown daemon
	opcontrol --shutdown


#----------------------------------------------------------------------------
# Documentation
#----------------------------------------------------------------------------
.PHONY: doc  # always remake, regardless of dependencies
doc: 
	(cat $(SRCDIR)/doc/Doxyfile; echo INPUT = $(SRCDIR)) | doxygen -

#----------------------------------------------------------------------------
# TAGS file generation for Emacs source code navigation
#----------------------------------------------------------------------------

.PHONY: etags
etags:
	/usr/bin/etags `find . -name '*.h' -o -name '*.c' -o -name '*.cpp'`

JUNK += TAGS

.PHONY: ctags
ctags:
	ctags `find . -name '*.h' -o -name '*.c' -o -name '*.cpp'`

JUNK += tags

#----------------------------------------------------------------------------
# Regenerate configure information.
#----------------------------------------------------------------------------

config_srcs = $(SRCDIR)/configure.in

$(SRCDIR)/configure: $(config_srcs)
	cd $(SRCDIR) && autoconf

# autoheader might not change config.h.in, so touch a stamp file.
$(SRCDIR)/config.h.in: $(SRCDIR)/stamp-h.in
$(SRCDIR)/stamp-h.in: $(config_srcs)
	cd $(SRCDIR) && autoheader
	date > $(SRCDIR)/stamp-h.in

config.h: stamp-h
stamp-h: $(SRCDIR)/config.h.in config.status
	./config.status

Makefile: $(SRCDIR)/Makefile.in config.status
	./config.status

Defines.mk: $(SRCDIR)/Defines.mk.in config.status
	./config.status

config.status: $(SRCDIR)/configure
	./config.status --recheck

CONFIG_JUNK += autom4te.cache config.h config.log config.status stamp-h Makefile Defines.mk

#----------------------------------------------------------------------------
# Clean up junk.
#----------------------------------------------------------------------------

.PHONY: tidy clean distclean
tidy:
	$(RM) $(JUNK)

clean: tidy
	$(RM) -r $(BINS) $(SIM_OUTPUT)
	$(RM) $(LIBTHREAD_DB) libthread_db.o 

distclean: clean
	$(RM) -r $(CONFIG_JUNK)
